<?php
namespace app\common\service;

use think\Page;
use app\common\logic\wechat\WechatUtil;
use think\facade\Db;

class Install extends Base {
    static $AUTH_CHECK = false; //默认必须登录

    public $auth_text = '';
    //获取压缩包链接
    public function index(){
        ini_set('memory_limit', '2048M'); // 增加内存限制
        ini_set('max_execution_time', 1200); // 增加超时时间
        set_time_limit(0);
        // 确保PHP错误显示
        ini_set('display_errors', 1);
        error_reporting(E_ALL);
        //生成源码压缩包
        $authname = input('authname');//授权主体
        $authkey = input('authkey');//授权密钥
        //数据库链接信息
        $db_host = input('db_host');
        $db_username = input('db_username');
        $db_password = input('db_password');
        $db_name = input('db_name');
        $db_prefix = input('db_prefix');
        $authdomain = input('authdomain');
        // 设置脚本执行时间
        @ignore_user_abort(true); // 忽略客户端断开连接

        // 验证授权信息
        $res_auth = $this->validateAuth($authkey,$authdomain);
        if (!$res_auth['status']) {
            //return $res_auth;
            //return ['status' => false, 'msg' => '授权验证失败'];
        }

        // 创建临时目录用于打包
        $tempDir = runtime_path() . 'install_temp/'.$authkey.'/';
        if (!is_dir($tempDir)) {
            mkdir($tempDir, 0755, true);
        }
        //生成授权信息
        $this->auth_text = '/**
 * UniTpShop（www.unitpshop.com） - 微信公众号小程序商城系统!
 * Copyright © 2025 www.unitpshop.com 保留所有权利
 * =========================================================
 * 版本：1.0
 * 授权主体：'.$authname.'
 * 授权域名：'.$authdomain.'
 * 授权码：'.$authkey.'
 * ----------------------------------------------
 * 您只能在商业授权范围内使用，不可二次转售、分发、分享、传播
 * 任何企业和个人不得对代码以任何目的任何形式的再发布
 * =========================================================
 */';

        // 创建zip文件
        $zipName = date('YmdHis') .$authkey. '_install.zip';
        $zipPath = public_path() . $zipName;
        $zip = new \ZipArchive();
        
        // 直接将文件添加到zip，跳过临时目录复制步骤
        if ($zip->open($zipPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) === true) {
            // 直接从源目录添加到zip，减少一次文件复制操作
            $this->addDirToZipDirect(root_path(), $zip, $this->auth_text);
            $zip->close();
            
            //获取压缩包大小
            $zipSize = filesize($zipPath);

            return [
                'status' => true,
                'msg' => '打包成功',
                'data' => [
                    'zip_path' => $zipPath,
                    'zip_name' => 'http://'.$_SERVER['HTTP_HOST'].'/'.$zipName,
                    'zip_size' => $zipSize,
                ]
            ];
        }

        return ['status' => false, 'msg' => '打包失败'];
    }

    /**
     * 验证授权信息
     */
    private function validateAuth( $authkey,$domain) {
        if(cache('auth_domain_'.$domain)){
            return [
                'status'=>1,
                'msg' => '授权成功',
            ];
        }
        $auth = Db::name('auth_domain')
            ->where('auth_code',$authkey)
            ->where('domain',$domain)
            ->find();
        if(!$auth){
            return [
                'status'=>0,
                'msg' => '未授权1',
            ];
        }
        if($auth['type']==1){
            //临时授权
            $end_time = $auth['w_time']+86400*7;
            if($end_time<time()){
                return [
                    'status'=>0,
                    'msg' => '临时授权已过期',
                ];
            }
        }else{
            if($auth['beian']==0){
                return [
                    'status'=>0,
                    'msg' => '域名未备案',
                ];
            }
            if($auth['status']==0){
                return [
                    'status'=>0,
                    'msg' => '未授权2',
                ];
            }
        }
        cache('auth_domain_'.$domain,1,3600);
        return [
            'status'=>1,
            'msg' => '授权成功',
        ];
    }

    /**
     * 复制目录
     */
    private function copyDir($source, $dest) {
        $excludes = [
             ROOT_PATH.'.git',
            ROOT_PATH.'.idea',
            ROOT_PATH.'runtime',
            ROOT_PATH.'public/upload',
            ROOT_PATH.'install.php',
            ROOT_PATH.'public/install',
            ROOT_PATH.'public/.user.ini',
            ROOT_PATH.'.env',
            ROOT_PATH.'.example.env',
            ROOT_PATH.'.gitignore',
            ROOT_PATH.'app/.htaccess',
            ROOT_PATH.'env.php',
            ROOT_PATH.'app/common/service/FileSync.php',
            ROOT_PATH.'app/common/service/Install.php',
//            ROOT_PATH.'admin/controller/Upgrade.php',
            ROOT_PATH.'public/static/design_page/fonts/msyh.ttf',
            ROOT_PATH.'basic/basic_old'
        ];
        if (!is_dir($dest)) {
            mkdir($dest, 0755, true);
        }
        
        // 批处理文件，每批处理的文件数
        $batchSize = 100;
        $fileCount = 0;
        
        $iterator = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS),
            \RecursiveIteratorIterator::SELF_FIRST
        );
        
        foreach ($iterator as $item) {
            $subPath = $iterator->getSubPathName();
            // 检查是否在排除列表中
            $shouldExclude = false;
            foreach ($excludes as $exclude) {
                // 统一使用正斜杠处理路径
                $exclude = str_replace('\\', '/', $exclude);
                $currentPath = str_replace('\\', '/', $item->getPathname());
                if (strpos($currentPath, $exclude) === 0 || $currentPath === $exclude) {
                    $shouldExclude = true;
                    break;
                }
            }
            if ($shouldExclude) {
                continue;
            }

            if ($item->isDir()) {
                if(!file_exists($dest . DIRECTORY_SEPARATOR . $subPath)){
                    mkdir($dest . DIRECTORY_SEPARATOR . $subPath, 0755, true);
                }
            } else {
                // 检查是否为controller目录下的PHP文件
                if (strpos($subPath, 'app'. DIRECTORY_SEPARATOR) !== false && pathinfo($item->getFilename(), PATHINFO_EXTENSION) === 'php') {
                    // 读取文件内容
                    $content = file_get_contents($item->getRealPath());
                    // 检查是否已有PHP标签
                    if (strpos($content, '<?php') === 0) {
                        // 在PHP标签后插入授权信息
                        $content = substr_replace($content, "\n" . $this->auth_text, 5, 0);
                    } else {
                        // 添加PHP标签和授权信息
                        $content = "<?php\n" . $this->auth_text . "\n" . $content;
                    }
                    // 写入新文件
                    file_put_contents($dest . DIRECTORY_SEPARATOR . $subPath, $content);
                } else {
                    copy($item, $dest . DIRECTORY_SEPARATOR . $subPath);
                }
            }
            
            $fileCount++;
            
            // 每处理一批文件，释放内存
            if ($fileCount % $batchSize === 0) {
                gc_collect_cycles(); // 强制垃圾回收
            }
        }
    }

    /**
     * 直接从源目录添加到zip，跳过临时目录复制步骤
     */
    private function addDirToZipDirect($source, $zip, $auth_text = '') {
        try {
            $excludes = [
                ROOT_PATH.'.git',
                ROOT_PATH.'.idea',
                ROOT_PATH.'runtime',
                ROOT_PATH.'public/upload',
                ROOT_PATH.'install.php',
                ROOT_PATH.'public/install',
                ROOT_PATH.'public/.user.ini',
                ROOT_PATH.'.env',
                ROOT_PATH.'.example.env',
                ROOT_PATH.'.gitignore',
                ROOT_PATH.'app/.htaccess',
                ROOT_PATH.'env.php',
                ROOT_PATH.'app/common/service/FileSync.php',
                ROOT_PATH.'app/common/service/Install.php',
                ROOT_PATH.'public/static/design_page/fonts/msyh.ttf',
                ROOT_PATH.'basic/basic_old'
            ];
            
            // 增加批处理文件数量，减少循环次数
            $batchSize = 500; // 增加批处理数量
            $fileCount = 0;
            $memoryLimit = 1024 * 1024 * 100; // 100MB内存限制阈值
            
            // 使用SPL迭代器直接处理文件
            $iterator = new \RecursiveIteratorIterator(
                new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS),
                \RecursiveIteratorIterator::SELF_FIRST
            );
            
            // 预先编译排除路径的正则表达式
            $excludePatterns = [];
            foreach ($excludes as $exclude) {
                $excludePatterns[] = str_replace('\\', '/', $exclude);
            }
            
            foreach ($iterator as $item) {
                // 检查是否在排除列表中
                $shouldExclude = false;
                $currentPath = str_replace('\\', '/', $item->getPathname());
                
                foreach ($excludePatterns as $pattern) {
                    if (strpos($currentPath, $pattern) === 0 || $currentPath === $pattern) {
                        $shouldExclude = true;
                        break;
                    }
                }
                
                if ($shouldExclude) {
                    continue;
                }
                
                // 获取相对路径
                $relativePath = str_replace(str_replace('\\', '/', $source), '', $currentPath);
                $relativePath = ltrim($relativePath, '/');
                
                // 跳过大文件（超过20MB的文件）
                if ($item->isFile() && $item->getSize() > 30 * 1024 * 1024) {
                    error_log('Skipping large file: ' . $item->getPathname());
                    continue;
                }
                
                if ($item->isDir()) {
                    if (!empty($relativePath)) {
                        $zip->addEmptyDir($relativePath);
                    }
                } else {
                    try {
                        // 检查是否为PHP文件并需要添加授权信息
                        if (!empty($auth_text) && strpos($relativePath, 'app/') === 0 && pathinfo($item->getFilename(), PATHINFO_EXTENSION) === 'php') {
                            $content = file_get_contents($item->getRealPath());
                            $tempFile = tempnam(sys_get_temp_dir(), 'zip_');
                            
                            // 检查是否已有PHP标签
                            if (strpos($content, '<?php') === 0) {
                                // 在PHP标签后插入授权信息
                                $content = substr_replace($content, "\n" . $auth_text, 5, 0);
                            } else {
                                // 添加PHP标签和授权信息
                                $content = "<?php\n" . $auth_text . "\n" . $content;
                            }
                            
                            // 写入临时文件
                            file_put_contents($tempFile, $content);
                            $zip->addFile($tempFile, $relativePath);
                            
                            // 注册一个关闭zip后删除临时文件的回调
                            register_shutdown_function(function() use ($tempFile) {
                                if (file_exists($tempFile)) {
                                    unlink($tempFile);
                                }
                            });
                        } else {
                            // 直接添加文件到zip
                            $zip->addFile($item->getRealPath(), $relativePath);
                        }
                    } catch (\Exception $e) {
                        error_log('Error adding file to zip: ' . $item->getRealPath() . ' - ' . $e->getMessage());
                    }
                }
                
                $fileCount++;
                
                // 每处理一批文件，检查内存使用并释放
                if ($fileCount % $batchSize === 0) {
                    // 检查内存使用情况
                    $memoryUsed = memory_get_usage();
                    if ($memoryUsed > $memoryLimit) {
                        gc_collect_cycles(); // 强制垃圾回收
                    }
                }
            }
        } catch (\Exception $e) {
            error_log('Error in addDirToZipDirect: ' . $e->getMessage());
            throw $e;
        }
    }

    /**
     * 删除目录 - 优化版本
     */
    private function deleteDir($dir) {
        if (!is_dir($dir)) {
            return;
        }
        
        try {
            // 批处理删除文件
            $batchSize = 200;
            $fileCount = 0;
            
            $iterator = new \RecursiveIteratorIterator(
                new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
                \RecursiveIteratorIterator::CHILD_FIRST
            );
            
            foreach ($iterator as $item) {
                try {
                    if ($item->isDir()) {
                        @rmdir($item->getRealPath());
                    } else {
                        @unlink($item->getRealPath());
                    }
                    
                    $fileCount++;
                    
                    // 每处理一批文件，释放内存
                    if ($fileCount % $batchSize === 0) {
                        gc_collect_cycles(); // 强制垃圾回收
                    }
                } catch (\Exception $e) {
                    error_log('Error deleting file: ' . $item->getRealPath() . ' - ' . $e->getMessage());
                    // 继续处理其他文件
                    continue;
                }
            }
            
            @rmdir($dir);
        } catch (\Exception $e) {
            error_log('Error in deleteDir: ' . $e->getMessage());
        }
    }

    public function check_domain(){
        $domain = input('domain');
        //检测域名是否授权
        $domain_path = pathinfo($domain);
        $domain = $domain_path['basename'];
        $exit = Db::name('auth_domain')->where('domain',$domain)->find();
        $log = [];
        $log['domain'] = $domain;
        $log['w_time'] = time();
        if(!$exit){
            //未授权的自动生成授权码，并给与一周使用时间
            $data = [];
            $data['domain'] = $domain;
            $data['name'] = '临时';
            $data['remark'] = '临时一周权限';
            $data['w_time'] = time();
            $data['type'] = 1;
            $rs = Db::name('auth_domain')->insert($data);
            $auth = ['type'=>1,'end_time'=>time()+7*86400];
            return ['status'=>true,'msg'=>'','auth'=>$auth];
        }else{
            if($exit['status']==0){
                $res = ['status'=>false,'msg'=>'域名未授权或已过期'];
            }else{
                $res = ['status'=>true,'msg'=>'已授权'];
            }

        }
        if($exit['type']==1){
            //临时授权只检测有效期，暂时不检测域名
            $end_time = $exit['w_time']+7*86400;
            if(time()>$end_time){
                $res = ['status'=>false,'msg'=>'授权已过期'];
            }
            $auth = ['type'=>1,'end_time'=>time()+7*86400];
            return ['status'=>true,'msg'=>'','auth'=>$auth];
        }
        $log['msg'] = $res['msg'];
        $check_time = time()-30*86400;//每月检测一次备案
        $cache = cache('check_beian_'.$domain);
        if($exit && ($exit['beian']==0 || $exit['beian_check_time']<=$check_time) && $cache!=1 && $res['status']){
            $res_beian = $this->check_beian($domain);
            Db::name('auth_domain')->where('domain',$domain)->update([
                'beian'=>$res_beian['status']?1:0,
                'beian_check_time'=>time(),
                'beian_msg'=>$res_beian['beian_result']??'',
                'beian_no'=>$res_beian['msg']??''
            ]);
            $res = $res_beian;
            $log['msg'] = $res_beian['msg'];
        }
        Db::name('auth_domain_log')->insert($log);
        //授权信息
        $res['auth'] = ['type'=>0,'end_time'=>0];
        $update = [
            'need_update' => 0,
            'new_version' => '',
            'update_text' => '',
            'update_time' => ''
        ];
        if(input('version')){
            //更新信息
            $last_version = Db::name('forum')->where('type','update')->order('id desc')->find();
            $update['new_version'] = $last_version['version'];
            $update['update_text'] = $last_version['content'];
            $update['update_time'] = date('Y-m-d H:i:s',$last_version['create_time']);
            if($last_version['version']!=input('version')){
                $update['need_update'] = 1;
            }
        }
        $res['update'] = $update;
        return $res;
    }
    public function checkcode() {
        $authname = input('authname');
        $authkey = input('authkey');
        $authdomain = input('authdomain');
        $exit = Db::name('auth_domain')->where('auth_code',$authkey)->where('domain',$authdomain)->find();
        if(!$exit){
            //不存在时创建临时授权
            $data = [];
            $data['domain'] = $authdomain;
            $data['name'] = $authname;
            $data['auth_code'] = $authkey;
            $data['remark'] = '临时一周权限';
            $data['w_time'] = time();
            $data['type'] = 1;
            $rs = Db::name('auth_domain')->insert($data);
            $auth = ['type'=>1,'end_time'=>time()+7*86400];
        }else{
            if($exit['type']==1){
                $end_time = $exit['w_time']+7*86400;
                if(time()>$end_time){
                    return ['status'=>false,'msg'=>'授权已过期'];
                }
                $auth = ['type'=>1,'end_time'=>$end_time];
            }else{
                $auth = ['type'=>0,'end_time'=>0];
            }
        }
        return ['status'=>1,'msg'=>'验证成功','auth'=>$auth];
    }
    public function getAuthKey(){
        $chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
        $str = '';
        $charCount = strlen($chars);
        for ($i = 0; $i < 15; $i++) {
            $randomIndex = rand(0, $charCount - 1);
            $str .= $chars[$randomIndex];
        }
        return ['status'=>1,'data'=>['authkey'=>$str]];
    }

    public function unlinkzip(){
        $name = input('name');
        $name = str_replace('http://'.$_SERVER['HTTP_HOST'], public_path(), $name);
        unlink($name);
    }
}
